3.2. 组件的组合使用-TodoList

功能: 组件化实现此功能

1. 显示所有todo列表

2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本

真没想到,这个例子居然对我来说这么难,从5月份停滞到现在8月份了,才有了一点感觉,敢看下去。很大部分原因还是得益于我最近一个星期看的redux项目,多多少少对react项目有了一些了解。

说实话,目前看来react项目确实很难(以后还有nextjs,前后端一起做,更难),很难就意味着会的人少一些,而国外项目很多都是react写的,我学会了不是更有竞争力吗?我现在一定要买车,我想涨个几千块钱的工资去买车,不买车不行了。

一直想在晚上或者周末的时候学习,但真的是学不进去,即使我是双休,今天想到了一个办法,我在手机上下载了B站,在上下班坐地铁的时候或者中午休息的时候可以试试看一看,反正坐地铁也是没有座位,每天看半小时(一周5天 * 0.5小时 * 56周 = 140小时,可以把数据结构与算法看好多遍了),碰到需要做笔记的地方,可以先手机截图,再写一些文字,然后晚上可以总结一下笔记。一个星期也可以学一些东西了。把react学完之后,就可以看韩老师的数据结构与算法了,很多东西其实我都忘记了,这可不行,得掌握核心科技。

3.1.4里面已经讲了,首先是拆分组件,这个todolist该怎么拆分呢?从vue的观点来看,这就是一整个组件,我写vue几年了,确实没有拆的这么细过,一个input也可以作为一个组件。但是react就是这样的,可以拆分的非常细。

按照3.1.4的步骤来做。

这里一个重要的知识点就是,props的更新会引起子组件的重新渲染,这一点想到了,做起来就容易了

1、拆分组件

将todolist拆分为4个组件:header组件、list组件、item组件、footer组件。创建这些文件夹和空文件即可。

image-20230807092716434

2、实现静态组件

可以自己实现一下,我就是这一步卡住了几个月的时间,怎么用react来实现?我以前还真的没有做过。其实我太担忧了,这一步不要想太多,就按照以前的知识,将组件写好,不用考虑实现任何交互,因为这一步就是静态页面,我就用class将这几个组件写好就够了,然后将这些组件组合起来,放在app.jsx里面,显示没有问题就行了。CSS可以大概写一下。

这里我直接使用老师提供的模板,在react全家桶资料\04_静态页面\todos_page里面,先将html代码全部放到app.js里面,将css代码全部放到app.css里面,然后将代码拆分到不同的组件里面,样式也带过去。静态显示没有问题就OK。

3、实现动态组件

3.1 动态显示初始化数据

这一步也很简单,通过分析,input组件里面的数据要添加到list里面去,但这两个组件是兄弟组件,现在还没有讲到兄弟组件的传值,只讲到了父组件传递数据到子组件,所以这一步先考虑将todos数据定义在app.jsx里面,传递给list组件,然后传递到item组件中。

3.2 交互

这一部分是最难的,因为涉及到子向父传值,之前没有遇到过。其实遇到过的,父组件可以向子组件传递数据,但也可以传递方法,那么子组件就可以通过调用这个方法,来达到传值给父组件的目的。

项目中涉及到给新添加的item赋id的值,不能是重复的,我想到了在app.jsx里面定义个变量,这个值一直增加即可。但是老师考虑到了实际项目的情况,所以推荐使用uuid库,npm i uuid。但是这个库有点大,所以使用了另一个库:nanoid,npm i nanoid,直接引入使用即可。

其实id的问题本不是前端应该考虑的,因为这个id应该是数据库里面的id,这里了解一下即可。

老师说的import引入文件的规范:

1、第三方的东西往上靠

2、自己的东西往下靠

3、样式放最后

具体代码就不展示出来了,只展示app.js里面的代码,如果不会就多做几遍,这个就是一个面试题。

老师最后总结了一下:

1.拆分组件、实现静态组件,注意:className、style的写法 2.动态初始化列表,如何确定将数据放在哪个组件的state中? ——某个组件使用:放在其自身的state中 ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升) 3.关于父子之间通信: 1.【父组件】给【子组件】传递数据:通过props传递 2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数 4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value 5.状态在哪里,操作状态的方法就在哪里---------比如说todos定义在app.js里面,那么操作todos的方法就全部写在app.js里面。

第4章:React ajax

4.1. 理解

4.1.1. 前置说明

  1. React本身只关注于界面, 并不包含发送ajax请求的代码
  2. 前端应用需要通过ajax请求与后台进行交互(json数据)
  3. react应用中需要集成第三方ajax库(或自己封装)

4.1.2. 常用的ajax请求库

  1. jQuery: 比较重, 如果需要另外引入不建议使用(这句话是什么意思?除了项目本身就是jQuery写的话,不要在别的项目里面使用jQuery)

  2. axios: 轻量级, 建议使用

    1)封装XmlHttpRequest对象的ajax

    2)promise风格

    3)可以用在浏览器端和node服务器端

4.2. axios

4.2.1. 文档

https://github.com/axios/axios

4.2.2. 相关API

老师从一个案例来讲解axios的使用方法:

先将 react全家桶资料\06_其他\测试代理服务器 用vscode打开,使用node server1.js打开服务器(不用担心第三方库,因为这个文件夹里面已经包含了,但是git已经忽略了node_modules文件夹,所以测试的时候还是需要从zip压缩包里面重新放入node_modules)。

新建一个react项目,安装axios:npm i axios,在app.js里面写入下面的代码:

点击按钮请求数据,显示如下:

image-20230808135436787

返回的是404。

这是为什么呢?因为ajax请求具有同源策略,服务器是http://localhost:5000,而react请求是http://localhost:3000,是不同源的。react的请求可以发送过去,但是返回结果会被ajax请求拒绝。所以需要使用代理服务器。

下面只讲了如何在react项目中使用代理服务器,但是没有讲代理服务器的原理(为什么会起作用),以后有时间再看吧。

方法1:

只能配置一个统一的代理。

在react项目的package.json文件里面进行配置:

返回结果:

image-20230808135727000

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)。

题外话:

axios请求资源并不是像我想象的那样会直接请求后端资源,而是根据请求的路径来查找,而同源策略的存在,说明肯定会查找到前端资源,比如这里的localhost:3000,就是前端项目的public文件夹所在的地址,如果在public找不到,才会进行请求转发。(前端项目都用到了webpack,所以这是webpack的规则)

方法2:

配置多个代理。另外开一个终端,输入node server2.js,让服务器2也运行起来。

需要在app.jsx同层级创建一个文件setupProxy.js,名字不能修改,里面必须写cjs,也就是Nodejs格式的代码,不能写es6格式的代码:

上面应该是react@17的写法,我安装的是react@18,所以写法有一些改变:

同时需要在app.js中将需要转发的接口地址进行修改:

点击按钮结果:

image-20230809090242566

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

 

GET请求

POST请求

4.3. 案例—github用户搜索

4.3.1. 效果

请求地址: https://api.github.com/search/users?q=xxxxxx

4.3.2实现

还是按照3.1.4的步骤来写:

还是一个重要的知识点,父组件传递给子组件的props如果更新了,那么就会引起子组件的重新渲染。

1、拆分组件

将组件拆分成Search和List组件。创建components文件夹,创建这两个组件。

2、实现静态组件

/react全家桶资料/04_静态页面/users_page 中拿到结构。先将html和css代码全部粘贴到App.js和App.css文件中(注意html的属性class要改为className,style的写法要改变等等),然后将 bootstrap.css 放到public文件夹中,新建一个css文件夹,放到其中,然后在index.html文件中引入bootstrap.css。

将App.js和App.css的代码进行拆分,静态显示没有问题就行了。

3、实现动态组件

3.1 动态显示初始化数据

安装axios,由于是search触发的请求,所以将请求写在Search组件里面。请求地址:https://api.github.com/search/users?q=xxxxxx

因为Search和List是兄弟组件,所以暂时需要借助app.js来进行数据传递。

然后List组件里面直接渲染即可。

3.2 交互

交互就是获取数据,Search里面已经写了。

一个知识点:

解构赋值的连续写法和解构赋值的重命名

 

但只做到这里是不够的,一:数据请求时应该有Loading显示,二:数据请求之前,应该展示初始的界面。怎么做?

暂时的考虑是在app.js里面定义一些变量,在search请求数据时,更改这些变量,这些变量实时传递到List中,用来判断展示不同的信息。

那么怎么根据不同的参数render不同的界面呢?这里老师使用的是三元表达式,多个三元表达式一起写。

 

4.4. 消息订阅-发布机制

  1. 消息订阅-发布机制有多种实现,但推荐使用工具库: PubSubJS。这个工具的使用非常简单,可以直接看npm里面的文档。

  2. 下载: npm install pubsub-js --save

  3. 使用:

    1)import PubSub from 'pubsub-js' //引入

    2)PubSub.subscribe('delete', function(msg,data){ }); //订阅,msg是定义的消息名,data是别的组件发布的数据。

    3)PubSub.publish('delete', data) //发布消息

 

那么就可以用pubsubjs来传递数据,重写github案例。那么App.jsx里面就不需要放任何状态和方法了,只放组件;Search和List直接进行通信,原来的状态其实都是List使用的,那么就放到List里面。

消息订阅的时机是什么?这一点很重要啊,就是List界面渲染的时候,也就是生命周期的componentDidMount(){}的时候。

消息发布的时机是什么?这个可以比较简单的想到,就是Search请求数据的时候。

这里还有一点非常重要,就是消息订阅是在componentDidMount(){}的时候订阅的,但是消息发布之后,会触发state的更新,此时组件会重新渲染,componentDidMount(){}会再次执行吗?试一下。

我的猜想是不会,因为此时走的是“更新时”的生命周期路线,setState触发的这条路线:

image-20240808215853599

项目完善:在componentDidMount里面订阅了消息,那么组件销毁时应该取消消息订阅,那么就需要这样做:

 

4.5.1. 文档

  1. Fetch API - Web API 接口参考 | MDN (mozilla.org)
  2. https://segmentfault.com/a/1190000003810652

说明:之前老师讲的jquery、axios都是基于xhr(XmlHttpRequest)技术实现的请求方法,fetch不是基于xhr,而是一个原生函数,可以直接使用,不用像axios那样安装、引入才能使用。

fetch的使用其实很简单,但是有一点需要特别注意:fetch的设计原则是“关注分离”,第一次请求回来的结果表示“是否联系服务器成功”,由于是Promise实现的,所以要将返回结果重新封装为一个Promise才能继续写下去。

我将github的案例中,fetch代替axios来实现数据请求。

要想清楚理解fetch的用法,必须把Promise深刻理解。

4.5.2. 特点

  1. fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
  2. 老版本浏览器可能不支持

4.5.3. 相关API

GET请求

POST请求